feat(admin): add Billing Platform admin page with invoice comparison#116269
Conversation
📊 Type Coverage Diff
🔍 5 new type safety issues introduced
This is informational only and does not block the PR. |
|
@sentry review |
|
@sentry review |
d91b0ed to
c89cc4e
Compare
c89cc4e to
8750267
Compare
8750267 to
a8f90aa
Compare
795c6a2 to
30b04ee
Compare
|
Tried force-pushing a clean history on top of base to make jest tests happy. 🤞🏻 |
30b04ee to
9db6f47
Compare
|
tried squashing all commits to one, maybe the lookback on the base branch detection will be happy with that? |
…G-86)
Adds a new "Billing Platform" sidebar entry hosting an Invoice Comparison
tool that calls the new
GET /api/0/_admin/cells/<cell_id>/invoice-comparison/ endpoint
(getsentry-side change in a paired PR).
The page lets staff pick a region + datetime-local time window and renders
per-org totals comparing legacy Invoice vs PlatformInvoice records
generated in the window, plus summary counts/totals/delta. Rows are
sorted by absolute $ delta desc. datetime-local inputs are converted to
UTC ISO on submit so the wire format is unambiguous regardless of the
operator's timezone.
The Billing Platform page is the landing spot for future milestone-1
verification tooling — invoice comparison is the first section.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(admin): show % delta column in invoice comparison
Pairs with the getsentry-side switch to percent-based sorting. Adds a
"Δ %" column showing percent delta relative to legacy (or ∞ when there's
no legacy baseline). Keeps the "Δ $" column so absolute-dollar context
remains visible.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(admin): right-align Δ columns in invoice comparison table
The Table descendant rule (`th, td { text-align: left }`) had
specificity (0,1,1) which beat the single-class RightHeader/RightCell
selectors at (0,1,0), so Legacy/Platform/Δ %/Δ $ columns silently
rendered left-aligned. Wrap the right-align rule in `&&` to double the
class selector and win specificity.
Addresses Cursor Bugbot finding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ref(admin): use scraps core components in Billing Platform page
Replaces custom styled() helpers with scraps primitives per AGENTS.md
frontend guidelines:
- Raw <h3> → <Heading as="h3"> from @sentry/scraps/text
- SummaryGrid (display: grid) → Grid columns="repeat(6, 1fr)"
- SummaryCell, Field (flex column) → Flex direction="column"
- SummaryLabel (font-size: sm + muted color) → Text size="sm" variant="muted"
- SummaryValue (font-size: lg + bold) → Text size="lg" bold
- SecondaryText, Count (small + muted color) → Text size="sm" variant="muted"
Kept FieldLabel and TruncatedNote as thin wrappers since they layer
small layout-only tweaks (color on a <label>, margin on a <Text>) that
don't have a direct primitive equivalent. Right-align CSS specificity
fix on RightHeader/RightCell stays as-is.
Addresses Cursor Bugbot finding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ref(admin): migrate invoice-comparison fetch to apiOptions + useQuery
Replaces deprecated `useApiQuery` (flagged by static/AGENTS.md frontend
guide) with the new pattern: `apiOptions.as<T>()` composed into
`useQuery` from TanStack Query. Uses `skipToken` for the path arg to
disable the query before the user submits, matching the previous
`enabled` semantics.
Addresses Cursor Bugbot finding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`sortedRows` was a no-op `useMemo` over `data.rows` — the component never sorted client-side, it just relied on the API's sort order. The name was misleading enough to confuse review tooling. Rename to `rows` and inline the fallback; the endpoint sorts before returning (see AdminInvoiceComparisonEndpoint + its test_sort_* tests) and a code comment documents the contract. Addresses Seer bot finding on PR #116269. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Jest 30 + SWC fails to parse scripts/genPlatformProductInfo.ts because the source uses `const __dirname = path.dirname(fileURLToPath(import.meta.url))` to recover __dirname under ESM. SWC's CJS transform emits that const verbatim, which collides with the `__dirname` Node already provides via the module wrapper — `SyntaxError: Identifier '__dirname' has already been declared`. This crash cascades during `--listTests --changedSince` traversal on PR runs, surfacing as a wave of "Your test suite must contain at least one test" errors against unrelated source files in worker logs and timing out the Jest shards at 30 min. Add `<rootDir>/scripts/` to both `testPathIgnorePatterns` and `modulePathIgnorePatterns` so Jest's discovery and module resolution both skip the directory. Nothing in scripts/ is a test, and nothing in static/* or tests/js/* imports from scripts/ (verified via grep). INTENT: this change should live in its own PR — it's CI infrastructure, not part of REVENG-86. Splitting after we confirm the Jest shards pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9db6f47 to
f00b3fe
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f00b3fe. Configure here.
| query: submitted ?? undefined, | ||
| staleTime: 0, | ||
| }), | ||
| }); |
There was a problem hiding this comment.
Region change triggers unintended automatic query refetch
Medium Severity
After an initial submission, changing the region dropdown immediately fires a new query with the old date range, bypassing the "Run comparison" button. This happens because region is read live from state into the query key (path and host), while dates are gated behind the submitted state. The inconsistency means region changes auto-refetch but date changes require the button — the region needs to be captured in submitted (or submitted reset on region change) to match the intended submit-gated behavior.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit f00b3fe. Configure here.
Jest 30 + SWC fails to parse `scripts/genPlatformProductInfo.ts`:
```
/scripts/genPlatformProductInfo.ts:51
const __dirname = _nodepath.dirname((0, _nodeurl.fileURLToPath)(require("url").pathToFileURL(__filename).toString()));
^
SyntaxError: Identifier '__dirname' has already been declared
```
The source uses `const __dirname = path.dirname(fileURLToPath(import.meta.url))`
to recover `__dirname` under ESM. SWC's CJS transform emits that const
verbatim, which collides with the `__dirname` Node already provides via
the module wrapper.
On PR runs where Jest goes through the `--listTests --changedSince`
path, that crash cascades — workers report a wave of "Your test suite
must contain at least one test" errors against unrelated source files
and the shards time out at 30 min. Symptom observed on
#116269 across multiple pushes; confirmed master
pushes are unaffected because they don't trigger the same traversal.
Add `<rootDir>/scripts/` to both `testPathIgnorePatterns` and
`modulePathIgnorePatterns` so Jest's discovery and module resolution
both skip the directory. Nothing in scripts/ is a test, and nothing
in static/* or tests/js/* imports from scripts/ (grep-verified).
A more durable fix would patch `scripts/genPlatformProductInfo.ts` to
rename its local `__dirname` binding (e.g. to `THIS_DIR`), but this
config change unblocks CI without modifying source files that other
infra (`pnpm gen:platform-info`) depends on.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>


Problem
Milestone-1 (consumer billing) ships invoices through the new platform code path in shadow mode alongside the legacy generator. Staff have no in-product way to spot-check parity between the two — confirming counts, dollar totals, and biggest divergences for invoices generated in a given window. Needed before we can confidently flip a population to platform-issued invoices.
Paired backend PR: getsentry#20425.
Solution
New "Billing Platform" page under `/_admin/billing-platform/`, surfaced as a sidebar entry between Invoices and Spike Projection Generation. First section is an Invoice Comparison tool; the page is intentionally a container for future milestone-1 verification tooling.
The tool itself:
Notes
REVENG-86